package com.opengw.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.Certificate;
import java.util.Enumeration;
import java.util.Iterator;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.opengw.exception.OpenGWException;

public class HTTPConnection {
	private URLConnection urlConnection;

	public HTTPConnection(String urlAddress) throws IOException, OpenGWException {
		URL url = new URL(urlAddress);
		if(urlAddress.startsWith("http")){
			this.urlConnection = (HttpURLConnection) url.openConnection();
		} else if(urlAddress.startsWith("https")){
			this.urlConnection = (HttpsURLConnection) url.openConnection();
		} else
			throw new OpenGWException(OpenGWException.UNSUPPORTED_PROTOCOL);
	}
	
	public HTTPConnection(HTTPRequest request) throws IOException, OpenGWException{
		URL url = new URL(request.getUrl());
		if(request.getUrl().startsWith("http")){
			this.urlConnection = (HttpURLConnection) url.openConnection();
		} else if(request.getUrl().startsWith("https")){
			this.urlConnection = (HttpsURLConnection) url.openConnection();
		} else
			throw new OpenGWException(OpenGWException.UNSUPPORTED_PROTOCOL);
		
		this.urlConnection.setDoInput(request.isDoInput());
		this.urlConnection.setDoOutput(request.isDoOutput());
		this.urlConnection.setReadTimeout(request.getReadTimeout());
		this.urlConnection.setConnectTimeout(request.getConnectTimeout());
		Iterator<String> keyIterator = request.getRequestHeaders().keySet().iterator();
		while (keyIterator.hasNext()) {
			String key = keyIterator.next();
			this.urlConnection.setRequestProperty(key, request.getRequestHeader(key));
		}
	}
	
	public void sendResponse(HttpServletResponse response) throws IOException{
		response.setStatus(getHTTPCode());
		setResponseHeaders(response);
		if(getHTTPCode() == 200){
			transferToHttpServletResponse(response);
		}
	}

	public void connect() throws IOException {
		this.urlConnection.connect();
	}
	
	public void setReadTimeout(int timeout){
		this.urlConnection.setReadTimeout(timeout);
	}

	public void setConnectTimeout(int timeout){
		this.urlConnection.setConnectTimeout(timeout);
	}
	
	public void setDoInput(boolean doInput) {
		this.urlConnection.setDoInput(doInput);
	}

	public void setDoOutput(boolean doOutput) {
		this.urlConnection.setDoOutput(doOutput);
	}

	public void setRequestHeader(String key, String value) {
		this.urlConnection.setRequestProperty(key, value);
	}

	public String getRequestHeader(String key) {
		return this.urlConnection.getRequestProperty(key);
	}

	public void setRequestHeaders(HttpServletRequest request) {
		Enumeration headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String key = (String) headerNames.nextElement();
			this.urlConnection.setRequestProperty(key, request.getHeader(key));
		}
	}

	public String getResponseHeader(String key) {
		return this.urlConnection.getHeaderField(key);
	}

	public void setResponseHeaders(HttpServletResponse response) {
		for (int i = 0;; i++) {
			String headerName = this.urlConnection.getHeaderFieldKey(i);
			String headerValue = this.urlConnection.getHeaderField(i);
			
			if(headerName == null && headerValue == null)
				break;
			response.setHeader(headerName, headerValue);
		}
	}
	
	public void setResponseHeaders(HTTPResponse response){
		for (int i = 0;; i++) {
			String headerName = this.urlConnection.getHeaderFieldKey(i);
			String headerValue = this.urlConnection.getHeaderField(i);
			
			if(headerName == null && headerValue == null)
				break;
			response.setResponseHeader(headerName, headerValue);
		}
	}
	
	public int getHTTPCode() throws IOException{
		return ((HttpURLConnection)this.urlConnection).getResponseCode();
	}
	
	public int getContentLength(){
		return this.urlConnection.getContentLength();
	}
	
	public String getContentType(){
		return this.urlConnection.getContentType();
	}
	
	public byte[] getContent() throws IOException{
		int contentLength = this.urlConnection.getContentLength();
		InputStream inputStream = this.urlConnection.getInputStream();
		if(contentLength != -1){
	        int len = 0;
	        int readContent = 0;
	        byte[] content = new byte[contentLength];
	        while(readContent < contentLength){
	            len = inputStream.read(content, readContent, contentLength - readContent);
	            readContent += len;
	        }
	        
	        return content;
		} else{
            int avaiableContentLength = inputStream.available();
            int totalContentLengh = avaiableContentLength;
            byte[] content = null;
            int len = 0;
            do{
                byte[] buffer = new byte[avaiableContentLength];
                int readContent = 0;
                while(readContent < avaiableContentLength){
    	            len = inputStream.read(buffer, readContent, avaiableContentLength - readContent);
    	            readContent += len;
    	        }
                try{
                	Thread.sleep(50);
                } catch (Exception e) {
				}
                avaiableContentLength = inputStream.available();
                if(avaiableContentLength > 0)
                    totalContentLengh += avaiableContentLength;
                if(content == null){
                	content = new byte[buffer.length];
                	System.arraycopy(buffer, 0, content, 0, buffer.length);
                } else {
                    byte[] tmpContent = content;
                    content = new byte[totalContentLengh];
                    System.arraycopy(tmpContent, 0, content, 0, tmpContent.length);
                    System.arraycopy(buffer, 0, content, tmpContent.length, buffer.length);
                }
                	

                
            } while(avaiableContentLength > 0);
            
            return content;
		}
	}
	
    public void transferToHttpServletResponse(HttpServletResponse response) throws IOException{
        InputStream inputStream = this.urlConnection.getInputStream();
        int contentLength = this.urlConnection.getContentLength();
		if(contentLength != -1){
	        int len = 0;
	        int readContent = 0;
	        byte[] buffer = new byte[contentLength];
	        while(readContent < contentLength){
	            len = inputStream.read(buffer);
	            response.getOutputStream().write(buffer, 0, len);
	            readContent += len;
	        }
		} else{
            int avaiableContentLength = inputStream.available();
            int len = 0;
            do{
                byte[] buffer = new byte[avaiableContentLength];
                int readContent = 0;
                while(readContent < avaiableContentLength){
    	            len = inputStream.read(buffer, readContent, avaiableContentLength - readContent);
    	            readContent += len;
    	        }
                try{
                	Thread.sleep(10);
                } catch (Exception e) {
				}
                avaiableContentLength = inputStream.available();
                response.getOutputStream().write(buffer);
            } while(avaiableContentLength > 0);
		}	
		
		response.getOutputStream().flush();
    }

    public void transferFromStream(InputStream transferStream, int contentLength) throws IOException{
        OutputStream outputStream = this.urlConnection.getOutputStream();
        int len = 0;
        int readContent = 0;
        byte[] data = new byte[8196];
        while(readContent < contentLength){
            len = transferStream.read(data);
            outputStream.write(data, 0, len);
            outputStream.flush();
            readContent += len;
        }
        closeOutputStream();
    }

    public void closeInputStream() throws IOException{
    	this.urlConnection.getInputStream().close();
    }

    public void closeOutputStream() throws IOException{
    	this.urlConnection.getOutputStream().close();
    }
    
    public Certificate[] getCertificates() throws SSLPeerUnverifiedException{
    	if(this.urlConnection instanceof HttpsURLConnection)
    		return ((HttpsURLConnection)this.urlConnection).getServerCertificates();
    	return null;
    }

}
